﻿namespace Pibby.Core
{
	using System;
	using System.Collections.Generic;
	using System.Data.Common;
	using System.Linq;
	using System.Reflection;
	using dogd;
	using Pibby.Core.IO;
	using Pibby.Core.Persistence;
	using Pibby.Core.PortableMedia;

	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Lite")]
	public class PodcastRepository :  Repository, Pibby.Core.Persistence.IPodcastRepository
	{
		private static readonly object lockingObject = new object();

		#region Constructors

		public PodcastRepository(string path)
			: base(new SqliteConnectionProvider(path),
				new MetadataProvider(), 
				new SqliteParameterFactory())
		{
		}
				
		#endregion Constructors

		#region Version

		public ApplicationVersion GetVersion()
		{
			var collection = this.ReadAll<ApplicationVersion>();

			if (collection != null && collection.Any())
			{
				return collection.First();
			}

			return null;
		}

		public void Update(ApplicationVersion v)
		{
			lock(lockingObject)
			{
				this.Update<ApplicationVersion>(v);
			}
		}

        public void UpdateDevelopment()
        {
			Assembly asm = typeof(PodcastRepository).Assembly;

			string sqlText = ResourceReader.ReadTextFromResource(asm, "Pibby.Core.Persistence.Development.sql");

			if (!string.IsNullOrEmpty(sqlText))
			{
				lock(lockingObject)
				{
					this.Execute(sqlText, RunScript);
				}
			}
		}

		#endregion Version
				
		#region Create New DB

		public void CreateNewDatabase()
		{
			Assembly asm = typeof(PodcastRepository).Assembly;

			string sql = ResourceReader.ReadTextFromResource(asm, "Pibby.Core.Persistence.DatabaseSchema.sql");
			this.CreateNewRepository(sql);

			sql = ResourceReader.ReadTextFromResource(asm, "Pibby.Core.Persistence.DatabaseInserts.sql");
			this.CreateNewRepository(sql);

			ApplicationVersion appVersion = new ApplicationVersion(Assembly.GetCallingAssembly().GetName().Version);
			this.Add<ApplicationVersion>(appVersion);
		}

		#endregion Create New DB

		#region Podcasts

		public PodcastCollection LoadPodcasts()
		{
			var all = this.ReadAll<Podcast>();

			// TODO - performance.
			PodcastCollection collection = new PodcastCollection();

			foreach (var podcast in all)
			{
				collection.Add(podcast);
			}

			collection.SortAlphabetically();

			return collection;
		}

		public PodcastCollection SearchPodcasts(string searchTerm)
		{
			Contract.EnsureIsNotNullOrEmpty(searchTerm, "Invalid search term");

			// TODO - performance.
			var all = this.Search<Podcast>(searchTerm);

			var collection = new PodcastCollection();

			foreach (var podcast in all)
			{
				collection.Add(podcast);
			}

			return collection;
		}

		public Podcast LoadPodcast(Key dbKey)
		{
			Contract.EnsureThat(dbKey.IsValid, "Invalid DbKey");

			return this.Read<Podcast>(dbKey);
		}

		[Obsolete("Don't use guid alternate")]
		public Podcast LoadPodcast(string guid)
		{
			Contract.EnsureIsNotNullOrEmpty(guid, "Invalid guid");

			return this.Read<Podcast>(guid);
		}

		public void Save(Podcast p)
		{
			lock(lockingObject)
			{
				if (p.IsDbKeyValid)
				{
					this.Update<Podcast>(p);
				}
				else
				{
					this.Add<Podcast>(p);
				}
			}

			Contract.EnsureThat(p.PrimaryKey.IsValid, "Podcast DbKey not created correctly");
		}

		public void Save(PodcastCollection existingCollection)
		{
			lock(lockingObject)
			{
				using (DbConnection connection = this.ConnectionProvider.Connection)
				{
					using (DbTransaction transaction = connection.BeginTransaction())
					{
						foreach (Podcast existingPodcast in existingCollection)
						{
							if (existingPodcast.IsDbKeyValid)
							{
								this.Update<Podcast>(existingPodcast, connection, transaction);
							}
							else
							{
								this.Add<Podcast>(existingPodcast, connection, transaction);
							}
						}

						transaction.Commit();
					}
				}
			}
		}

		public void Remove(Podcast p)
		{
			Contract.EnsureThat(p.IsDbKeyValid, "Invalid primary key");

			lock(lockingObject)
			{
				using (DbConnection connection = this.ConnectionProvider.Connection)
				{
					using (DbTransaction transaction = connection.BeginTransaction())
					{
						this.RemoveAll<Episode>(p.PrimaryKey, connection, transaction);
						this.Remove<Podcast>(p.PrimaryKey, connection, transaction);

						transaction.Commit();
					}
				}
			}
		}

		#endregion 

		#region Episodes

		public EpisodeCollection LoadEpisodes()
		{
			return new EpisodeCollection(this.ReadAll<Episode>().ToList());
		}

		public EpisodeCollection LoadEpisodes(EpisodeLifecycle status)
		{
			FilterByCollection filters = new FilterByCollection();

			if (status == EpisodeLifecycle.NotDownloaded)
			{
				filters.Add(new FilterBy(Episode.StageColumn, FilterOperator.LessThanOrEqual, (int)status));
				//			filters.Add(new FilterBy(Episode.AgeColumn, FilterOperator.NotEqual, (int)EpisodeAge.BackCatalog));
			}
			else
			{
				filters.Add(new FilterBy(Episode.StageColumn, FilterOperator.Equal, (int)status));
			}

			return new EpisodeCollection(this.ReadAll<Episode>(filters).ToList());
		}
		
		public EpisodeCollection LoadEpisodes(Key podcastKey)
		{
			Contract.EnsureThat(podcastKey.IsValid, "Invalid primary key");

			return new EpisodeCollection(this.ReadAll<Episode>(podcastKey).ToList());
		}

		public EpisodeCollection LoadEpisodes(IEnumerable<int> dbKeys)
		{
			var collection = new EpisodeCollection();

			foreach (int dbKey in dbKeys)
			{
				collection.Add(this.LoadEpisode(new Key(dbKey)));
			}

			return collection;
		}

		public EpisodeCollection SearchEpisodes(string searchTerm)
		{
			Contract.EnsureIsNotNullOrEmpty(searchTerm, "Invalid search term");

			return new EpisodeCollection(this.Search<Episode>(searchTerm).ToList());
		}

		[Obsolete("Dont use this version")]
		public EpisodeCollection SearchEpisodes(Key podcastKey, string searchTerm)
		{
			Contract.EnsureThat(podcastKey.IsValid, "Invalid primary key");
			Contract.EnsureIsNotNullOrEmpty(searchTerm, "Invalid search term");

			var all = this.Search<Episode>(searchTerm);

			var collection = new EpisodeCollection();

			foreach (var episode in all)
			{
				// short cut for now!!!
				if (episode.SubscriptionID == podcastKey.Value)
					collection.Add(episode);
			}

			return collection;
		}

		public Episode LoadEpisode(Key dbKey)
		{
			Contract.EnsureThat(dbKey.IsValid, "Invalid DbKey");

			return this.Read<Episode>(dbKey);
		}

		[Obsolete("Dont use the guid version")]
		public Episode LoadEpisode(string guid)
		{
			Contract.EnsureIsNotNullOrEmpty(guid, "Invalid guid");

			return this.Read<Episode>(guid);
		}

		public void Save(Episode episode)
		{
			lock (lockingObject)
			{
				if (episode.IsDbKeyValid)
				{
					this.Update<Episode>(episode);
				}
				else
				{
					this.Add<Episode>(episode);
				}
			}

			Contract.EnsureThat(episode.PrimaryKey.IsValid, "Episode DbKey not created correctly");
		}

		public void Import(EpisodeCollection importedCollection)
		{
			if (importedCollection.Count > 0)
			{
				var podcastKey = new Key (importedCollection[0].SubscriptionID);

				var existingEpisodes = this.LoadEpisodes(podcastKey);

				foreach (var episode in existingEpisodes)
				{
					if (importedCollection.Contains(episode))
						importedCollection.Remove(episode);
				}
			}

			if (importedCollection.Count == 0)
				return;

			lock (lockingObject)
			{
				using (DbConnection connection = this.ConnectionProvider.Connection)
				{
					using (DbTransaction transaction = connection.BeginTransaction())
					{
						foreach (Episode candidateEpisode in importedCollection)
						{
							this.Add<Episode>(candidateEpisode, connection, transaction);
						}

						transaction.Commit();
					}
				}
			}
		}

		#endregion Episodes
        
        public MediaPlayerSettings LoadMediaSettings()
        {
            var all = this.ReadAll<MediaPlayerSettings>().ToList();

            if (all.Count > 0)
                return all[0];

			return new MediaPlayerSettings()
				{
					VolumeName = "STONE PLUS",
					EjectOption = EjectOption.Auto,
					OrganizationOption = OrganizationOption.Split,
					TransferOption = TransferOption.Auto,
					Enabled = true,
					BaseFolder = "1_Podcasts"
				};
        }

        public void Save(MediaPlayerSettings settings)
        {
			lock(lockingObject)
			{
			    if (settings.IsDbKeyValid)
			    {
				    this.Update<MediaPlayerSettings>(settings);
				}
			    else
			    {
				    this.Add<MediaPlayerSettings>(settings);
			    }
			}

			Contract.EnsureThat(settings.PrimaryKey.IsValid, "Media Device DbKey not created correctly");
		}
    }
}
